#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <math.h>
#include "3D.h"

#if _WIN32
#   include <Windows.h>
#endif
#if __APPLE__
#   include <OpenGL/gl.h>
#   include <OpenGL/glu.h>
#   include <GLUT/glut.h>
#else
#   include <GL/gl.h>
#   include <GL/glu.h>
#   include "glut.h"
#endif

#include "sine_func.h"

float dt = 0.0f;
bool keysDown[256] = { 0 };

int mousex  = 0;
int mousey  = 0;
int mousedx = 0;
int mousedy = 0;
bool mouseOneDown = false;
bool mouseTwoDown = false;
bool mouseThreeDown = false;

GridMeshXZ terrain(0.05f, 300, 300);

void print_gl_errors()
{
    GLenum error = glGetError();
        if (error)
        {
            const char* error_string = (const char*)gluErrorString(error);
            std::cerr << "opengl error " << error_string;
        }
}

void drawAxes(float x, float y, float z, float scale)
{
    glBegin(GL_LINES);

    glColor3f(1.0f, 0.0f, 0.0f);
    glVertex3f(x, y, z);
    glVertex3f(x + scale, y, z);

    glColor3f(0.0f, 1.0f, 0.0f);
    glVertex3f(x, y, z);
    glVertex3f(x, y + scale, z);

    glColor3f(0.0f, 0.0f, 1.0f);
    glVertex3f(x, y, z);
    glVertex3f(x, y, z + scale);

    glEnd();
}

void drawAxes()
{
    glBegin(GL_LINES);

    glColor3f(1.0f, 0.0f, 0.0f);
    glVertex3f(0.0f, 0.0f, 0.0f);
    glVertex3f(1.0f, 0.0f, 0.0f);
    glColor3f(0.0f, 1.0f, 0.0f);
    glVertex3f(0.0f, 0.0f, 0.0f);
    glVertex3f(0.0f, 1.0f, 0.0f);
    glColor3f(0.0f, 0.0f, 1.0f);
    glVertex3f(0.0f, 0.0f, 0.0f);
    glVertex3f(0.0f, 0.0f, 1.0f);

    glEnd();
}

void mouseMove(int x, int y)
{
    // mousex & mousey are still the previous tick's values
    mousedx = x - mousex;
    mousedy = y - mousey;

    mousex = x;
    mousey = y;

}

void mouseEvent(int button, int state, int x, int y)
{
    switch (button)
    {
    case GLUT_LEFT_BUTTON:
        GLUT_DOWN ? mouseOneDown = true : mouseOneDown = false;
        break;

    case GLUT_RIGHT_BUTTON:
        GLUT_DOWN ? mouseTwoDown = true : mouseTwoDown = false;
        break;

    case GLUT_MIDDLE_BUTTON:
        GLUT_DOWN ? mouseThreeDown = true : mouseThreeDown = false;
        break;
    }
}

void keyDown(unsigned char key, int x, int y)
{
    keysDown[key] = true;
}

void keyUp(unsigned char key, int x, int y)
{
    keysDown[key] = false;
}

void display()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    drawAxes(0.0f, 0.0f, -10.0f, 1.0f);

    glBegin(GL_TRIANGLES);
    glColor3f(1.f, 0.f, 0.f);
    for (int i = 0; i < terrain.getIndexSize(); ++i)
    {
        Vec3 vert = terrain[i];
        glVertex3f(vert.x, vert.y, vert.z);
    }
    glEnd();

    print_gl_errors();
    glutSwapBuffers();
}

void resize(int width, int height)
{
    glViewport(0, 0, width, height);
    std::cerr << "window resize, width = " << width << " height = " << height;
    glMatrixMode(GL_PROJECTION);   
    glLoadIdentity();            
    double aspect = (double)width / (double)height;
    gluPerspective(45.f, aspect, 0.05f, 1000.f);            
    glMatrixMode(GL_MODELVIEW); 

}

void idle()
{
////////////////////////////////////////////
    static float timeLastCall = 0.0f; 
///////////////////////////////////////////

    // Convert milliseconds to seconds as a float
    float timeThisCall = (float)glutGet(GLUT_ELAPSED_TIME) / 1000.0f; 

    dt = timeThisCall - timeLastCall;
    if (dt > 0.03)
        dt = 0.03;

    glutPostRedisplay();

////////////////////////////////////////////
    timeLastCall = timeThisCall;
///////////////////////////////////////////
}

int main(int argc, char **argv)
{
    glutInit(&argc, argv);

    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);

    glutCreateWindow("ISYS");

    glOrtho(-1, 1, -1, 1, -1, 1);

    glEnable(GL_DEPTH_TEST);

    /*glFrontFace(GL_CCW);
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);*/

    glutDisplayFunc(display);
    glutIdleFunc(idle);
    glutReshapeFunc(resize);
    glutPassiveMotionFunc(mouseMove);
    glutMotionFunc(mouseMove);
    glutMouseFunc(mouseEvent);

    glutIgnoreKeyRepeat(true);
    glutKeyboardFunc(keyDown);
    glutKeyboardUpFunc(keyUp);

    glutMainLoop();

    return EXIT_SUCCESS;
}